<script setup>
import { ref, nextTick, watch } from 'vue'
import { ChatDotRound, Close, Position, Delete } from '@element-plus/icons-vue'
import {
  aiStreamCharService,
  aiBlockHistoryListService,
  aiBlockHistoryService,
  aiDeleteHistoryService
} from '@/api/languagemodel'

const isOpen = ref(false)
const message = ref('')
// 消息数组，存储用户和AI的消息
const messages = ref([])
// 当前AI回复的消息引用
const currentAiMessage = ref(null)
const currentUserMesseage = ref(null)
const scrollbarRef = ref(null)
//自动滚动到底部
const scrollToBottom = async () => {
  await nextTick()
  if (!scrollbarRef.value || !scrollbarRef.value.wrapRef) {
    return
  }
  const scrollWrap = scrollbarRef.value.wrapRef
  // 计算最大滚动距离 = 总高度 - 可视高度
  const maxScrollTop = scrollWrap.scrollHeight - scrollWrap.clientHeight

  // 调用组件API滚动到底部
  scrollbarRef.value.setScrollTop(maxScrollTop)
}
//获取详细历史数据
const sidebarList = ref([])
const selectedChatId = ref(null)
const chatID = ref()
const getHistoryDetail = async (chatId) => {
  const { data } = await aiBlockHistoryService({
    userId: '123456',
    chatId: chatId
  })
  chatID.value = chatId
  data.map((item, index) => (item.id = +new Date() + index))

  data.forEach((item, index) => {
    if (index % 2 !== 0) {
      item.content = JSON.parse(item.content).message
    }
  })
  messages.value = [...data]
  selectedChatId.value = chatId
  scrollToBottom()
}
//历史消息列表
const toggleAssistant = async () => {
  if (!isOpen.value && !messages.value.length) {
    const { data } = await aiBlockHistoryListService('123456')
    if (data.length === 0) {
      await getHistoryDetail('0')
      const { data } = await aiBlockHistoryListService('123456')
      sidebarList.value = data
      await getHistoryDetail(sidebarList.value[0])
      isOpen.value = !isOpen.value
    } else {
      isOpen.value = !isOpen.value
      sidebarList.value = data
      await getHistoryDetail(sidebarList.value[0])
    }
  } else {
    isOpen.value = !isOpen.value
  }
}

//子传父跳转页面名字
const emit = defineEmits(['jumpPage'])

// 监控状态管理
const isAITestRunning = ref(false)
const aiTestChatId = ref(null)
const hasChangedChat = ref(false)
const isInputDisabled = ref(false)
const showDisabledTip = ref(false)

// 监控chatId变化
watch(chatID, (newChatId, oldChatId) => {
  // 清空输入框
  message.value = ''

  // 只有在aiTest运行期间进行监控
  if (!isAITestRunning.value) return

  // 第一次检测到chatId变化
  if (!hasChangedChat.value && newChatId !== aiTestChatId.value) {
    hasChangedChat.value = true
  }
  // 当切换回原会话时
  else if (hasChangedChat.value && newChatId === aiTestChatId.value) {
    // 检查最后元素的isStreaming状态
    const lastMessage = messages.value[messages.value.length - 1]
    //由于用户没有isStreaming状态，所以无论如何检测到都会创建一个新的
    if (lastMessage && !lastMessage.isStreaming) {
      const aiMessage = {
        id: Date.now(),
        role: 'assistant',
        content: '',
        isStreaming: true
      }
      messages.value.push(aiMessage)
      // 重新禁用输入框
      isInputDisabled.value = true
    }
    hasChangedChat.value = false
  }
})

const aiTest = async () => {
  // 设置监控状态
  isAITestRunning.value = true
  aiTestChatId.value = chatID.value
  hasChangedChat.value = false
  isInputDisabled.value = true

  // 创建新的AI消息
  const aiMessage = {
    id: Date.now(),
    role: 'assistant',
    content: '',
    isStreaming: true
  }
  messages.value.push(aiMessage)
  currentAiMessage.value = { ...aiMessage }

  const response = await aiStreamCharService({
    userId: '123456',
    userMessage: currentUserMesseage.value.content,
    chatId: chatID.value
  })
  if (hasChangedChat.value) {
    isAITestRunning.value = false
    isInputDisabled.value = false
    return
  }
  //停止监控
  isAITestRunning.value = false
  // 获取响应文本并正确处理编码
  const reader = response.body.getReader()
  const textDecoder = new TextDecoder()
  let fullBuffer = '' // 累积处理后的完整内容
  let messageStart = false // 是否找到message字段开始位置
  let messageProcessedLength = 0 // 已显示的message长度
  // 新增状态：记录message的完整结束索引（一旦找到就固定）
  let messageEndIdx = -1
  let hasFoundEnd = false // 标记是否已找到结束位置
  const targetPrefix = '\\"message\\": \\"'

  // 新增：文本队列（存储待打字的片段）
  const textQueue = []
  // 新增：状态标记（是否正在打字）
  let isTyping = false
  // 目标对话框（使用当前消息的索引）
  const target = messages.value[messages.value.length - 1]

  while (true) {
    const { done, value } = await reader.read()
    let str = textDecoder.decode(value, { stream: true })
    const newStr = fixMergedChunk(str)

    fullBuffer += newStr
    if (!hasFoundEnd) {
      if (!messageStart) {
        // 找"message": "的位置（找到后标记开始）
        messageStart = fullBuffer.includes(targetPrefix)
      }

      if (messageStart) {
        // 从"message": "之后提取内容，直到下一个"结束
        const startIdx = fullBuffer.indexOf(targetPrefix) + targetPrefix.length

        // 直接找startIdx之后的第一个"，找到就停
        messageEndIdx = fullBuffer.indexOf('"', startIdx)
        if (messageEndIdx > -1) {
          hasFoundEnd = true // 标记已找到，后续不再查找
        }

        // 提取当前已传输的message内容（处理未结束的情况）
        const currentEnd =
          messageEndIdx > -1 ? messageEndIdx : fullBuffer.length
        let messageContent = fullBuffer.substring(startIdx, currentEnd)
        // 计算新增内容
        const newText = messageContent.slice(messageProcessedLength)
        if (newText) {
          //取消气泡效果
          messages.value[messages.value.length - 1].isStreaming = false

          const cleanedText = newText.endsWith('\\')
            ? newText.slice(0, -1) // 移除最后一个字符\
            : newText
          // 关键1：新文本先加入队列，不立即处理
          textQueue.push(cleanedText)

          // 关键2：如果当前没有在打字，就开始处理队列
          if (!isTyping) {
            processQueue()
          }

          messageProcessedLength += newText.length
        }
      }
    }
    // 强制更新DOM
    await new Promise((resolve) => setTimeout(resolve, 0))
    if (done) {
      //停止禁用
      isInputDisabled.value = false
      //去除可能存在的无用信息
      fullBuffer = fullBuffer
        .replace(/^\s*```json\s*/, '')
        // 去除结尾的 ``` 及前面的换行（允许前后有空白）
        .replace(/\s*```\s*$/, '')

      const obj = JSON.parse(JSON.parse(`"${fullBuffer}"`))
      currentAiMessage.value.content = obj
      break
    }
  }

  //清理分块函数
  function fixMergedChunk(mergedChunk) {
    // 正则：专门找"内容"这种结构，提取里面的内容（忽略包装）
    // 简单说：匹配所有被"包裹的部分，取出中间的内容
    const regex = /"((?:\\.|[^"\\])*)"/g
    let contentList = [] // 存拆出来的内容

    // 循环拆出所有内容
    let match
    while ((match = regex.exec(mergedChunk)) !== null) {
      contentList.push(match[1]) // match[1]就是每个块里的内容（去掉了包装）
    }

    // 拼接所有内容，得到干净的结果
    return contentList.join('')
  }
  
  //打字机
  function processQueue() {
    // 如果队列空了，标记为不在打字，检查是否需要跳转
    if (textQueue.length === 0) {
      isTyping = false

      // 打字机效果完成，检查是否需要跳转
      if (currentAiMessage.value && currentAiMessage.value.content.execute) {
        // 如果是删除操作（operation === 2），不传递参数
        if (currentAiMessage.value.content.operation === 2) {
          emit('jumpPage', '')
        } else {
          const parameter = currentAiMessage.value.content.parameter
          emit('jumpPage', parameter)
        }
        isOpen.value = false
      }

      return
    }

    // 标记为正在打字
    isTyping = true

    // 从队列头部取出第一个待处理片段
    const currentText = textQueue.shift()

    let i = 0
    const timer = setInterval(() => {
      if (i < currentText.length) {
        // 逐字添加到目标
        target.content += currentText[i++]
        scrollToBottom()
      } else {
        // 当前片段打字结束，清除定时器
        clearInterval(timer)
        // 递归处理下一个片段（自动等待当前结束）
        processQueue()
      }
    }, 100) // 打字速度
  }
}

const sendMessage = () => {
  if (message.value.trim()) {
    // 添加用户消息
    const userMessage = {
      id: Date.now(),
      role: 'user',
      content: message.value.trim()
    }
    messages.value.push(userMessage)

    // 保存当前消息并清空输入框
    currentUserMesseage.value = { ...userMessage }
    message.value = ''
    // 调用AI接口
    aiTest()
  }
  scrollToBottom()
}

// 处理禁用状态下的点击事件
const handleDisabledClick = () => {
  // 如果没有文字，不执行任何操作
  if (!message.value.trim()) {
    return
  }

  if (isInputDisabled.value) {
    showDisabledTip.value = true
    // 3秒后自动隐藏提示
    setTimeout(() => {
      showDisabledTip.value = false
    }, 3000)
  } else {
    // 如果不是禁用状态，执行发送
    sendMessage()
  }
}

const handleCreateChat = async () => {
  getHistoryDetail(`${+Math.max(...sidebarList.value) + 1}`)
  const res = await aiBlockHistoryListService('123456')
  sidebarList.value = res.data
}
const deleteWarn = ref(false)
const handleDeleteChat = async (item) => {
  if (sidebarList.value.length <= 1) {
    deleteWarn.value = true
    setTimeout(() => {
      deleteWarn.value = false
    }, 2000)
    return
  }
  await aiDeleteHistoryService({
    userId: '123456',
    chatId: item
  })
  const res = await aiBlockHistoryListService('123456')
  sidebarList.value = res.data
}
</script>
<template>
  <div class="ai-assistant">
    <!-- 悬浮按钮 -->
    <div
      class="ai-button"
      :class="{ active: isOpen }"
      @click="toggleAssistant"
      title="AI助手"
    >
      <el-icon v-if="!isOpen" class="ai-icon">
        <ChatDotRound />
      </el-icon>
      <el-icon v-else class="ai-icon">
        <Close />
      </el-icon>
    </div>

    <!-- AI助手抽屉 -->
    <el-drawer
      v-model="isOpen"
      direction="rtl"
      :with-header="false"
      size="50vw"
      custom-class="ai-drawer"
      :show-close="false"
    >
      <div class="ai-drawer-flex">
        <!-- 左侧侧边栏 -->
        <el-scrollbar class="sidebar-scrollbar">
          <div v-if="deleteWarn" class="delete-warn">会话唯一时不可删除</div>
          <div class="sidebar-list">
            <!-- 新建会话item -->
            <div
              class="sidebar-item sidebar-item-new"
              @click="handleCreateChat"
            >
              <div class="sidebar-title">+ 新建会话</div>
            </div>
            <!-- 普通会话item -->
            <div
              class="sidebar-item"
              v-for="item in sidebarList"
              :key="item"
              :class="{ 'sidebar-item-selected': selectedChatId === item }"
              @click="getHistoryDetail(item)"
            >
              <div class="sidebar-title">会话{{ +item + 1 }}</div>
              <el-icon class="delete-icon" @click.stop="handleDeleteChat(item)">
                <Delete />
              </el-icon>
            </div>
          </div>
        </el-scrollbar>
        <!-- 右侧聊天内容 -->
        <el-scrollbar
          max-height="96vh"
          class="scrollbar-wrapper"
          ref="scrollbarRef"
        >
          <div class="ai-content">
            <div class="ai-header">
              <div class="ai-title">
                <el-icon class="ai-header-icon">
                  <ChatDotRound />
                </el-icon>
                <span>AI助手</span>
              </div>
              <el-button type="text" @click="toggleAssistant" class="close-btn">
                <el-icon><Close /></el-icon>
              </el-button>
            </div>
            <div class="chat-container">
              <!-- 消息列表 -->
              <div class="messages-list">
                <div
                  v-for="msg in messages"
                  :key="msg.id"
                  :class="['message-item', msg.role + '-message']"
                >
                  <div
                    :class="[
                      'message-content',
                      msg.role === 'assistant' ? 'ai-bubble' : ''
                    ]"
                  >
                    <div class="message-text">{{ msg.content }}</div>
                    <div v-if="msg.isStreaming" class="streaming-dot">
                      <span></span>
                      <span></span>
                      <span></span>
                    </div>
                  </div>
                </div>
              </div>
              <!-- 欢迎消息 -->
              <div v-if="messages.length === 0" class="welcome-message">
                <el-icon class="welcome-icon">
                  <ChatDotRound />
                </el-icon>
                <p>你好！我是AI助手，有什么可以帮助你的吗？</p>
              </div>
            </div>
            <div class="input-area">
              <el-input
                v-model="message"
                placeholder="请输入你的问题..."
                class="message-input"
                @keyup.enter="handleDisabledClick"
              >
                <template #append>
                  <el-button
                    type="primary"
                    @click="handleDisabledClick"
                    :disabled="!message.trim()"
                  >
                    <el-icon><Position /></el-icon>
                  </el-button>
                </template>
              </el-input>

              <!-- 自定义提示 -->
              <div v-show="showDisabledTip" class="disabled-tip">
                当前存在处理，请等待处理结束
              </div>
            </div>
          </div>
        </el-scrollbar>
      </div>
    </el-drawer>
  </div>
</template>

<style scoped lang="less">
.ai-assistant {
  position: fixed;
  bottom: 20px;
  right: 20px;
  z-index: 9999;
}

// 悬浮按钮样式
.ai-button {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  box-shadow: 0 4px 20px rgba(102, 126, 234, 0.4);
  transition: all 0.3s ease;
  position: relative;
  overflow: hidden;

  &::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: linear-gradient(135deg, #764ba2 0%, #667eea 100%);
    opacity: 0;
    transition: opacity 0.3s ease;
    border-radius: 50%;
  }

  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 25px rgba(102, 126, 234, 0.6);

    &::before {
      opacity: 1;
    }
  }

  &.active {
    background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
    box-shadow: 0 4px 20px rgba(255, 107, 107, 0.4);

    &::before {
      background: linear-gradient(135deg, #ee5a24 0%, #ff6b6b 100%);
    }
  }

  .ai-icon {
    color: white;
    font-size: 24px;
    z-index: 1;
    position: relative;
  }
}

// AI助手抽屉样式
:deep(.ai-drawer) {
  top: 0 !important;
  right: 0 !important;
  height: 100vh !important;
  border-radius: 0 !important;
  box-shadow: 0 10px 40px rgba(0, 0, 0, 0.18);
  background: transparent !important;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  z-index: 9998;
}
.scrollbar-wrapper {
  height: 100%;

  :deep(.el-scrollbar__wrap) {
    overflow-x: hidden;
  }
  :deep(.el-scrollbar__view) {
    height: 100%;
  }
}

.ai-content {
  width: 100%;
  min-height: 100%;
  background: pink;
  border-radius: 0 0 0 0;
  box-shadow: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  position: relative;
}
.scrollbar-wrapper {
  height: 100%;

  :deep(.el-scrollbar__wrap) {
    overflow-x: hidden;
  }
}

.ai-header {
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  height: 48px;
  background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
  color: white;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-radius: 0;
  padding: 0 20px;
  z-index: 2;
}

.ai-title {
  display: flex;
  align-items: center;
  font-size: 18px;
  font-weight: 600;

  .ai-header-icon {
    margin-right: 8px;
    font-size: 20px;
  }
}

.close-btn {
  color: white;
  padding: 4px;
  background: transparent;
  border: none;
  box-shadow: none;
  margin-left: 8px;

  &:hover {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 4px;
  }
}

.chat-container {
  flex: 1;
  padding: 68px 20px 20px 20px; // 顶部预留空间给悬浮header
  overflow-y: auto;
  background: #fff5f5;

  .messages-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .message-item {
    display: flex;
    max-width: 80%;

    &.user-message {
      align-self: flex-end;

      .message-content {
        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        color: white;
        border-radius: 18px 4px 18px 18px;
      }
    }

    &.ai-message {
      align-self: flex-start;

      .message-content {
        background: white;
        color: #333;
        border-radius: 4px 18px 18px 18px;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
      }
    }
  }

  .message-content {
    padding: 12px 16px;
    position: relative;

    .message-text {
      line-height: 1.5;
      font-size: 14px;
      white-space: pre-wrap;
      word-break: break-word;
    }

    .streaming-dot {
      display: flex;
      align-items: center;
      margin-top: 8px;

      span {
        width: 6px;
        height: 6px;
        background: #999;
        border-radius: 50%;
        margin-right: 4px;
        animation: wave 1.2s ease-in-out infinite;

        &:nth-child(2) {
          animation-delay: 0.2s;
        }

        &:nth-child(3) {
          animation-delay: 0.4s;
          margin-right: 0;
        }
      }
    }
  }

  @keyframes wave {
    0%,
    100% {
      transform: translateY(0);
      opacity: 0.6;
    }
    50% {
      transform: translateY(-5px);
      opacity: 1;
    }
  }
}

.welcome-message {
  display: flex;
  align-items: center;
  padding: 16px;
  background: white;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  margin-bottom: 16px;

  .welcome-icon {
    color: #ff6b6b;
    font-size: 24px;
    margin-right: 12px;
  }

  p {
    margin: 0;
    color: #333;
    font-size: 14px;
    line-height: 1.5;
  }
}

.input-area {
  padding: 16px 20px;
  background: white;
  border-top: 1px solid #f0f0f0;
  border-radius: 0 0 16px 16px;
  position: relative;
}

.disabled-tip {
  position: absolute;
  top: -85vh;
  left: 50%;
  transform: translateX(-50%);
  background: rgba(255, 0, 0, 0.9);
  color: white;
  padding: 8px 16px;
  border-radius: 6px;
  font-size: 14px;
  z-index: 10000;
  animation: fadeInOut 3s ease-in-out;
  white-space: nowrap;
  pointer-events: none;
  border: 2px solid white;
}

@keyframes fadeInOut {
  0% {
    opacity: 0;
    transform: translateX(-50%) translateY(10px);
  }
  10% {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
  90% {
    opacity: 1;
    transform: translateX(-50%) translateY(0);
  }
  100% {
    opacity: 0;
    transform: translateX(-50%) translateY(-10px);
  }
}

.message-input {
  .el-input-group__append {
    background: #ff6b6b;
    border-color: #ff6b6b;

    .el-button {
      color: white !important;
      border: none !important;
      background: #ff6b6b !important;

      &:hover {
        background: #ee5a24 !important;
      }

      &:disabled {
        background: #ccc !important;
        color: #999 !important;
      }

      // 当有文字时保持黄色（无论是否禁用）
      &:not(:disabled) {
        background: #ff6b6b !important;
      }
    }
  }
}

// 响应式设计
@media (max-width: 900px) {
  .ai-panel {
    width: 90vw;
    min-width: unset;
    max-width: unset;
    top: 0;
    height: 100vh;
  }
  .ai-content {
    border-radius: 0;
  }

  .message-item {
    max-width: 90%;
  }
}

.ai-drawer-flex {
  display: flex;
  height: 100%;
}
.sidebar-scrollbar {
  width: 25%;
  min-width: 180px;
  background: #f7f7fa;
  border-right: 1px solid #eee;
}
.sidebar-list {
  padding: 16px 0;
}
.sidebar-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: #fff;
  margin: 0 12px 16px 12px;
  border-radius: 8px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.04);
  padding: 12px;
  cursor: pointer;
  transition:
    box-shadow 0.2s,
    background 0.2s,
    color 0.2s,
    transform 0.2s;
}
.sidebar-item:hover {
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
  transform: translateY(-2px);
}
.sidebar-item-selected {
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  color: white;
  box-shadow: 0 4px 16px rgba(102, 126, 234, 0.3);
  transform: translateY(-2px);
}
.sidebar-item-selected .sidebar-title {
  color: white;
}
.sidebar-item-selected .sidebar-desc {
  color: rgba(255, 255, 255, 0.8);
}
.sidebar-item-new {
  background: #409eff;
  color: #fff;
  margin-bottom: 12px;
  cursor: pointer;
}
.sidebar-item-new .sidebar-title {
  color: #fff;
}
.sidebar-item-new:hover {
  background: #66b1ff;
}
.delete-icon {
  color: #ff4d4f;
  font-size: 18px;
  margin-left: 8px;
  cursor: pointer;
  transition: color 0.2s;
}
.delete-icon:hover {
  color: #ff7875;
}
.sidebar-title {
  font-weight: bold;
  font-size: 16px;
}
.sidebar-desc {
  color: #888;
  font-size: 12px;
  margin-top: 4px;
}
.scrollbar-wrapper {
  width: 75%;
}
.sidebar-actions {
  display: flex;
  gap: 8px;
  margin: 16px 12px 8px 12px;
}
.action-btn {
  flex: 1;
  border-radius: 20px;
  font-weight: bold;
  font-size: 14px;
  height: 36px;
  padding: 0;
}
.new-btn {
  background: #409eff;
  color: #fff;
  border: none;
}
.new-btn:hover {
  background: #66b1ff;
  color: #fff;
}
.delete-btn {
  background: #ff4d4f;
  color: #fff;
  border: none;
}
.delete-btn:hover {
  background: #ff7875;
  color: #fff;
}
.message-item.ai-message {
  align-self: flex-start;
  display: flex;
}
:deep(.message-item.ai-message) .message-content {
  background: #fff !important;
  color: #333 !important;
  border-radius: 18px 18px 18px 4px !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
  padding: 12px 16px !important;
  max-width: 80% !important;
  min-width: 48px !important;
  word-break: break-word !important;
  display: inline-block !important;
  margin: 0 0 0 4px !important;
}
:deep(.ai-bubble) {
  background: #fff !important;
  color: #333 !important;
  border-radius: 18px 18px 18px 4px !important;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08) !important;
  padding: 12px 16px !important;
  max-width: 80% !important;
  min-width: 48px !important;
  word-break: break-word !important;
  display: inline-block !important;
  margin: 0 0 0 4px !important;
}

.delete-warn {
  background: #fffbe6;
  color: #d46b08;
  border: 1px solid #ffe58f;
  border-radius: 6px;
  padding: 8px 12px;
  margin: 12px;
  font-size: 14px;
  text-align: center;
  z-index: 12000;
  position: relative;
  animation: fadeInOut 2s;
}
@keyframes fadeInOut {
  0% {
    opacity: 0;
  }
  10% {
    opacity: 1;
  }
  90% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
</style>
